package ephrecorder

import (
	"dgo/goutils/gnss"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/golang/gocache"
	"gitee.com/ymofen/replay"
	"gnssa/pkg/idset"
	"sort"
	"strings"
	"time"
)

type EphRecorder struct {
	rd            *replay.TimeRotatingFile
	svDec         *gnss.GnssSVEpochRtcmDecoder
	ephDec        *gnss.Rtcm3RawBufDecoder
	ttlCache      gocache.CacheOperator
	lastEpochTime time.Time
	obsCache      gobase.BytesBuilder
	interval      int64
}

func NewEphRecorder(saveFmt string) *EphRecorder {
	er := &EphRecorder{
		rd:       replay.NewTimeRotatingFile(),
		svDec:    gnss.NewGnssSVEpochRtcmDecoder(),
		ephDec:   gnss.NewRtcm32RawBufDecoder("ephRd"),
		interval: 120,
	}
	er.rd.SetSaveFormat(saveFmt)
	er.svDec.OnEpochFunc = er.onEpochFunc
	er.svDec.OnRtcmBuf = er.onObsRtcmBuf
	er.ephDec.OnRtcmBuf = er.onEphRtcmBuf
	er.ttlCache = gocache.NewCache()
	return er
}

func (er *EphRecorder) Close() error {
	return er.rd.Close()
}

func (er *EphRecorder) InputObsBuf(buf []byte) {
	er.svDec.InputBuf(buf)
}

func (er *EphRecorder) InputEphBuf(buf []byte) {
	er.ephDec.InputBuf(buf)
}

func (er *EphRecorder) onObsRtcmBuf(msgid uint16, buf []byte) bool {
	if gnss.RtcmIsObsMsm(int(msgid)) || msgid == 1005 || msgid == 1006 || msgid == 1033 {
		er.obsCache.Appendf("msgid=%d&buf=%s\n", msgid, gobase.BufToHexStr(buf, 0, ""))
		return true
	}
	return false
}

func statSignalMap(epoch *gnss.GnssSVEpoch) []string {
	nowTime := time.Now()
	staMap := idset.NewManager()
	epoch.RangeAll(func(navisys byte, satid uint16, obs *gnss.GnssSVObs) bool {
		if obs.IsValid() {
			sysCode := gnss.GetNavSysCode(navisys)
			obs.RangeSignal(func(signal *gnss.GnssSVSignal) bool {
				if signal.IsValid() {
					staMap.Add(obs.GetPrnCode(), fmt.Sprintf("%s_%s", sysCode, signal.GetSignalCode()), nowTime)
				}
				return true
			})
		}
		return true
	})
	signMap := idset.NewManager()
	staMap.Range(func(id string, set []string) bool {
		signKey := strings.Join(set, ",")
		signMap.Add(signKey, id, nowTime)
		return true
	})

	strs := make([]string, 0, signMap.Count())
	signMap.Range(func(id string, set []string) bool {
		strs = append(strs, fmt.Sprintf("%s=%s", id, strings.Join(set, ",")))
		return true
	})
	sort.Strings(strs)
	return strs
}

func (er *EphRecorder) onEpochFunc(epoch *gnss.GnssSVEpoch, checktype int) {
	nowTime := time.Now().UTC()
	te := epoch.DecodeTime(nowTime)
	defer func() {
		er.obsCache.Cleanup()
	}()

	if er.interval > 0 {
		if !er.lastEpochTime.IsZero() && te.Sub(er.lastEpochTime) < time.Duration(er.interval)*time.Second {
			return
		}
		er.lastEpochTime = te
	}

	var wb gobase.BytesBuilder

	strs := statSignalMap(epoch)

	sort.Strings(strs)
	wb.Appendf("# utctime=%s&epoch=1\n", gobase.DateTimeString(te))
	for _, s := range strs {
		wb.Appendf("# %s\n", s)
	}
	wb.Write(er.obsCache.Bytes())
	wb.WriteString("\n\n")
	er.rd.WriteWithNowTime(nowTime, wb.Bytes())

}

func MakeEphCaption(eph *gnss.RtcmEph) string {
	return fmt.Sprintf("%s-%d-%s", eph.GetPrnStrCode(), eph.MsgId, eph.RnxTime().Format("20060102150405"))
}

func (er *EphRecorder) onEphRtcmBuf(buf []byte) {
	msgId := gnss.GetRtcmMsgId(buf, 24)
	if gnss.RtcmIsEph(msgId) {
		key := fmt.Sprintf("%s-%d", gobase.MD5Buf(buf), gobase.CRC32Buf(buf))
		if _, ok := er.ttlCache.Get(key); ok {
			return
		}
		eph, _ := gnss.DecodeRtcmEph(buf, 24)
		if eph == nil {
			return
		}
		er.ttlCache.SetWithTTL(key, 1, time.Minute)
		eph.DecodeEphTime()
		nowtime := time.Now().UTC()
		var wb gobase.BytesBuilder
		wb.Appendf("#eph=%s&utctime=%s\n", MakeEphCaption(eph),
			gobase.DateTimeString(nowtime))
		wb.AppendStr("buf=")
		wb.AppendStr(gobase.BufToHexStr(buf, 0, ""))
		wb.AppendStr("\n")
		er.rd.WriteWithNowTime(nowtime, wb.Bytes())
	}
}
